#pragma once

#include <utils/config.h>

namespace solar::cuda
{
    class Receiver
    {

        public:
            virtual void init(float pixel_per_meter_for_receiver) = 0;
            virtual void setResolution(float pixel_per_meter_for_receiver) = 0;
            virtual __host__ __device__ auto getFocusCenter(const float3& heliostat_position) -> float3 = 0;
            /// @brief Allocate the final image matrix.
            void allocImage();
            /// @brief Clean the final image matrix.
            void cleanImageContent() const;
            void clear();
            __device__ Receiver() = default;
            __host__ Receiver(cudaStream_t stream = nullptr);
            __device__ __host__ Receiver(const Receiver& rec);
            __device__ __host__ ~Receiver();

            int type_; // rectangle receiver:0   cylinder receiver:1
            float3 normal_;
            float3 pos_;
            float3 size_;
            int surface_index_; // the index of receiving surface
            float pixel_length_;
            float* d_image_ = nullptr;   // On GPU, memory size = resolution_.x * resolution_.y
            int2 resolution_;  // x means column, y means row
            int n_resolution_; // for rectangular/cylindrical is resolution_.x * resolution_.y
                               // for imagePlane, additionally add resolution_.x + resolution_.y
            float* d_total_reflect_ = nullptr;
            cudaStream_t stream_ = nullptr;
            bool external_stream_ = false;
            void setCudaStream(const cudaStream_t& stream);
    };
} // namespace solar::cuda

#define SOLAR_CUDA_FORCE_INLINE
#if defined(SOLAR_CUDA_STANDALONE_COMPILE) && !defined(SOLAR_BUILD_RECEIVER)
    #define SOLAR_CUDA_FORCE_INLINE ALWAYS_INLINE
    #define SOLAR_CUDA_INLINE inline
    #include "Receiver.cu"
#endif
